﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace NumericalMethod
{

   public partial class NonLinearForm : ContentPane
   {
      private StatusForm mStatus;
      private VParser FormulaParser = new VParser();//= new Parser();
      double val1, val2, val3, val4, tol;
      int maxIteration, mStep;
      const string separator = "==================================";
      const string lineSptor = "----------------------------------";
      private string formatfPoint = "f4";//will be replaced by config file.
      public NonLinearForm()
      {
         InitializeComponent();
         this.Notes = NoteType.NonLinearSystem;
      }
      private void SetFloatingPoint(double err)
      {
         int fPoint = 4;//default is 4
         if (Math.Abs(err) < 1)
         {
            err = 1 / err;
            fPoint = 0;
            while (err > 1)
            {
               err /= 10;
               fPoint++;
            }
         }
         formatfPoint = "f" + fPoint;
      }
      private bool initCalc()
      {
         ////bisection_method();
         ////mStatus.append("Hello");
         //string TextError="";
         if (textfx.Text.Equals(""))
         {
            MessageBox.Show("f(x) cannot be empty");
            textfx.Focus();
            return false;
         }
         if (x1text.Text.Equals(""))
         {
            MessageBox.Show("x1 cannot be empty");
            x1text.Focus();
            return false;
         }
         if (maxIterationText.Text.Equals(""))
         {
            MessageBox.Show("Max iteration cannot be empty");
            maxIterationText.Focus();
            return false;
         }
         if (tolerenceText.Text.Equals(""))
         {
            MessageBox.Show("Tolerence cannot be empty");
            tolerenceText.Focus();
            return false;
         }
         if (dxText.Text.Equals("") && methodComboBox.SelectedIndex == 2)
         {
            MessageBox.Show("df/dx cannot be empty");
            tolerenceText.Focus();
            return false;
         }
         mStatus.AppendOutput(separator);
         mStatus.AppendOutput(methodComboBox.SelectedItem + " method");
         mStatus.AppendOutput("f(x) = " + textfx.Text);
         mStatus.AppendOutput("x1 = " + x1text.Text);
         if (methodComboBox.SelectedIndex == 0)
            mStatus.AppendOutput("x2 = " + x2text.Text);

         mStatus.AppendOutput("Maximum number of iterations = " + maxIterationText.Text);
         mStatus.AppendOutput("Tolerance (%) = " + tolerenceText.Text);

         mStatus.AppendOutput(separator);
         mStatus.AppendOutput("Checking equation...");

         //
         // Bisection rules
         //
         if (methodComboBox.SelectedIndex == 0)
         {
            if (x2text.Text.Equals(""))
            {
               MessageBox.Show("x2 cannot be empty");
               x2text.Focus();
               return false;
            }
            double fx2 = 0;
            double fx1 = getFX(double.Parse(x1text.Text));

            fx2 = getFX(double.Parse(x2text.Text));
            mStatus.AppendOutput("f(x1) = " + fx1);
            mStatus.AppendOutput("f(x2) = " + fx2);
            if (fx1 * fx2 >= 0)
            {
               mStatus.AppendOutput("f(x1)*f(x2)<0 condition is not satisfied.");
               return false;
            }
         }

         //set value
         maxIteration = int.Parse(maxIterationText.Text);
         val1 = double.Parse(x1text.Text);
         if (methodComboBox.SelectedIndex == 0)
            val2 = double.Parse(x2text.Text);
         else
            val2 = 0;
         tol = double.Parse(tolerenceText.Text);
         SetFloatingPoint(tol);
         val4 = 0.0;
         val3 = val4;
         mStep = 0;

         return true;
      }

      public override void OnLoad()
      {
         mStatus = new StatusForm();
         Parent.Controls.Add(mStatus);
         methodComboBox.SelectedIndex = 0;

      }

      private void PerformIteration_Click(object sender, EventArgs e)
      {
         if (initCalc())
         {
            if (methodComboBox.SelectedIndex == 0)
               bisectionMethod();
            else if (methodComboBox.SelectedIndex == 1)
               simpleFixedPoint();
            else if (methodComboBox.SelectedIndex == 2)
               newtonRaphson();
         }
      }

      private void SetupMethod()
      {
         switch (methodComboBox.SelectedIndex)
         {

            case 0:
               dxText.Enabled = false;
               x2text.Enabled = true;
               label1.Text = "f(x)";
               mStatus.SetColumn(new string[] { "n", "x0", "x1", "xm", "f(x0)", "f(xm)", "| x1-xm |" });
               break;
            case 1:
               dxText.Enabled = false;
               x2text.Enabled = false;
               x1text.Text = "1";
               label1.Text = "g(x)";
               mStatus.SetColumn(new string[] { "i", "xi", "| xi-x_(i-1) |" });
               chkMsgBoxSfi window = new chkMsgBoxSfi();
               window.ShowDialog();
               break;
            case 2:
               dxText.Enabled = true;
               x2text.Enabled = false;
               label1.Text = "f(x)";
               mStatus.SetColumn(new string[] { "i", "xi", "xi-x_(xi-1)", "f(xi)", "f'(xi)" });
               break;
         }
      }

      private double getFX(double x)
      {
         string formula = textfx.Text.Replace("x", x.ToString());
         try
         {
            return FormulaParser.Parse(formula);
         }
         catch (Exception)
         {
            MessageBox.Show("Specified text sequence not allowed");
            textfx.Focus();
            return 0;
         }
      }
      private double getDX(double x)
      {
         string formula = dxText.Text.Replace("x", x.ToString());
         try
         {
            return FormulaParser.Parse(formula);
         }
         catch (Exception)
         {
            MessageBox.Show("Specified text sequence not allowed");
            dxText.Focus();
            return 0;
         }
      }
      private void bisectionMethod()
      {
         double error = Math.Abs(val1 - val2);
         mStatus.ClearRow();
         while (error > tol && mStep < maxIteration)
         {
            mStep++;
            val3 = (val1 + val2) / 2;
            double j = getFX(val1) * getFX(val3);
            error = Math.Abs(val2 - val3);
            mStatus.InsertRow(new string[] { 
                    mStep.ToString(),
                    val1.ToString(formatfPoint),
                    val2.ToString(formatfPoint),
                    val3.ToString(formatfPoint),
                    getFX(val1).ToString(formatfPoint),
                    getFX(val3).ToString(formatfPoint),
                    error.ToString(formatfPoint) 
                });
            if (j < 0)
               val2 = val3;
            else if (j > 0)
               val1 = val3;
            else
               break;
         }
         mStatus.AppendOutput(String.Format("Iteration: {0}\r\nFinal root:{1}", mStep, val3));
      }
      private void simpleFixedPoint()
      {
         double? error;
         mStatus.ClearRow();
         error = null;

         do
         {
            mStatus.InsertRow(new string[] { 
                    mStep.ToString(), 
                    val1.ToString(formatfPoint),
                    (error!=null)?error.Value.ToString(formatfPoint):""
                });
            val2 = val1;

            val1 = getFX(val1);
            error = Math.Abs(val1 - val2);
            mStep++;
         } while (error > tol && mStep < maxIteration);
         mStatus.InsertRow(new string[] { 
                    mStep.ToString(), 
                    val1.ToString(formatfPoint),
                    error.Value.ToString(formatfPoint)
                });
         mStatus.AppendOutput(String.Format("Iteration: {0}\r\nFinal root:{1}", mStep, val1));
      }
      private void newtonRaphson()
      {
         // calculate next term f(x) / f'(x) then subtract from current root
         mStatus.ClearRow();
         double error;
         do
         {
            error = Math.Abs(val1 - val3);
            mStatus.InsertRow(new string[] {
                    mStep.ToString(),
                    val1.ToString(formatfPoint),
                    error.ToString(formatfPoint),
                    getFX(val1).ToString(formatfPoint),
                    getDX(val1).ToString(formatfPoint)
                });
            val3 = val1;
            val2 = getFX(val1) / getDX(val1);
            val1 -= val2;
            mStep++;
         } while (error > tol && mStep < maxIteration);
         mStatus.AppendOutput(String.Format("Iteration: {0}\r\nFinal root:{1}", mStep, val1));

      }
      private void methodComboBox_SelectedIndexChanged(object sender, EventArgs e)
      {
         SetupMethod();
      }
   }
}
